﻿#pragma once
#include<Windows.h>
#include<iostream>
#include<fstream>
#include "MutableSolver.h"
#include "ABSolver.cpp"
#include "MCSolver.h"
#include "FinalSolver.h"
#include<easyx.h>
#include "graphics.h"
#include <string>
#include <thread>
#include <mutex>
using std::thread;
using std::ofstream;

#define BLUE_ 2
#define RED_ 1

//配置命名空间, 应该用枚举的, 但是不想改了
namespace cfg
{
    constexpr auto NOT_RECORD = -1;
}



//------------------这些好像是GUI相关, 我也不知道为什么写在这里-------------------------//
char tex5[1000];
//flag 是限制按钮的 video_flag 限制图片最后显示
int flag = 0;
int video_flag = 0;
struct OP
{
    int dir = -1;
    int x = -1;
    int y = -1;

} op;

struct Button //按钮类
{
    int x1;
    int x2;
    int y1;
    int y2;
};

void setVertexAndShowButton(Button btn, int x1, int y1, int twidth, int tlength, TCHAR text[1000], int zh, int zw) //设置按钮的位置，大小
{
    //设置顶点坐标
    btn.x1 = x1;

    btn.y1 = y1;

    //绘制按钮边框
    setbkmode(TRANSPARENT);
    setfillcolor(RGB(192, 192, 192));
    fillroundrect(btn.x1, btn.y1, btn.x1 + twidth, btn.y1 + tlength, 10, 10);
    settextcolor(BLACK);
    settextstyle(zh, zw, _T("思源字体"));
    int tx = x1 + (twidth - textwidth(text)) / 2;
    int ty = y1 + (tlength - textheight(text)) / 2;

    outtextxy(tx, ty, text);
}

//---------------------以上------------------------------------//




class GameManager
{
public:
    int turn = 1; //回合
    int red = 1; //红方玩家代表数值
    int blue = 2; //蓝方玩家代表数值
    int red_score = 0;
    int blue_score = 0;
    Board* board;
    std::vector<Step> valid_steps; //有效步数
    std::vector<ScoreStep> score_steps; //分值记录
    int eve_flag = 0;
    int pve_flag = 0;
    TemplateSolver* solver_1;
    TemplateSolver* solver_2;
    GameManager()
    {
        this->blue_score = 0;
        this->red_score = 0;
        this->board = new Board();
        this->solver_1 = new ABSolver();
        this->solver_2 = new ABSolver();
    }

    GameManager(TemplateSolver* s1, TemplateSolver* s2) : solver_1(s1), solver_2(s2)
    {
        board = new Board();
    }


    GameManager(TemplateSolver* s1) : solver_1(s1)
    {
        board = new Board();
        this->solver_2 = new ABSolver();
    }

    ~GameManager()
    {
        delete solver_1;
        delete solver_2;
    }

    Frame* get_frame()
    {
        return new Frame(turn, red_score, blue_score, board);
    }

    //还原棋盘
    void restart(int turn)
    {
        this->valid_steps.clear();
        this->score_steps.clear();
        valid_steps.shrink_to_fit();
        score_steps.shrink_to_fit();
        this->blue_score = 0;
        this->red_score = 0;
        this->turn = turn;
        this->board->refresh_board();
    }

    //判断游戏是否结束
    bool is_game_over()
    {
        if (this->red_score + this->blue_score == 25) {

            record_info_persistent("red_player", "blue_player");

            return true;
        }
        else {
            return false;
        }
    }

    //返回谁赢了
    int get_winner()
    {
        return this->red_score > this->blue_score ? this->red : this->blue;
    }

    //换手
    void change_round()
    {

        //检查一遍格子得到当前玩家得了多少分
        int score = board->check_box(this->turn);
        //记录得分情况, 方便撤回
        this->score_steps.push_back(ScoreStep{ this->turn, score });
        score_steps.shrink_to_fit();
        if (score == 0)
        {
            if (turn == red)
                turn = blue;
            else
                turn = red;
        }
        else
        {
            if (turn == red)
                red_score += score;
            else
                blue_score += score;
        }
    }

    // 检验步伐是否合法
    bool is_step_illegal(Step step)
    {
        return board->check_illegal(step);
    }

    //控制台输入
    Step console_cin()
    {
        int dir, x, y;
        while (true)
        {
            //创建句柄
            HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出句柄
            SetConsoleTextAttribute(handle, 2);
            printf("[info]: 人类玩家请输入步伐:");
            SetConsoleTextAttribute(handle, 7);
            puts("");
            std::cin >> dir >> x >> y;
            if (is_illegal(dir, x, y))
            {
                break;
            }
            else
            {
                SetConsoleTextAttribute(handle, 4);
                printf("[error]: 步数非法，请重新键入！！！ ");
                SetConsoleTextAttribute(handle, 7);
                puts("");
            }
        }
        Step step;
        step.dir = dir;
        step.x = x;
        step.y = y;
        return step;
    }


    Step ai_cin_1()
    {
        Frame* frame = get_frame();
        Step step = this->solver_1->solve(frame);
        return step;
    }


    Step ai_cin_2()
    {
        Frame* frame = get_frame();
        Step step = this->solver_2->solve(frame);
        return step;
    }



    Step player_cin()
    {

        op.dir = -1;
        op.x = -1;
        op.y = -1;
        int count = 0;
        while (true)
        {
            mouseEvent();
            if (op.dir != -1 && op.x != -1 && op.y != -1)
            {
                if (is_illegal(op.dir, op.x, op.y))
                {
                    count = 0;
                    break;
                }
                else
                {
                    if (count == 0)
                    {
                        printf("[erro]: 坐标错误，请重新点击\n");
                        count = 1;
                    }
                }
            }
        }
        Step step;
        step.dir = op.dir;
        step.x = op.x;
        step.y = op.y;
        return step;
    }

    //合法性判断
    bool is_illegal(int dir, int x, int y)
    {
        if (dir < 0 || dir > 1 || x < 0 || x > 5 || y < 0 || y > 4)
            return false;
        if (board->line[dir][x][y] == 0)
            return true;
        return false;
    }

    //自动游戏回合, 带GUI, 若要开启这个new出GameManager后调用这个函数就行了
    void auto_game_loop()
    {
        init_graph();
        sprintf_s(tex5, "红方得分 %d : 蓝方得分 %d", red_score, blue_score);
        show_score(20, 5, 20, 10, tex5);
        clearrectangle(517, 52, 700, 72);
        show_who_step(517, 52, 20, 10);
        show_where(100, 5, 20, 10);
        Step step;
        step.dir = -1;
        step.x = -1;
        step.y = -1;
        while (true)
        {
            if (flag == 0)
            {
                while (true)
                {
                    mouseEvent();
                    if (eve_flag == 1)
                    {
                        if (video_flag == 1)
                        {
                            video_flag = 0;
                            restart(0);
                            clearrectangle(30, 52, 444, 480);
                            draw();
                        }
                        break;
                    }
                    else if (pve_flag == 1)
                    {
                        if (video_flag == 1)
                        {
                            video_flag = 0;
                            restart(0);
                            clearrectangle(30, 52, 444, 480);
                            draw();
                        }
                        break;
                    }
                }
                flag = 1;
            }
            //EVE
            if (eve_flag == 1)
            {
                if (turn == red)
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出句柄
                    SetConsoleTextAttribute(handle, 4);
                    printf("[info]: 当前为红方");
                    puts("");
                    step = ai_cin_1();
                    SetConsoleTextAttribute(handle, 7);
                }
                else
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出句柄
                    SetConsoleTextAttribute(handle, 1);
                    printf("[info]: 当前为蓝方");
                    puts("");
                    step = ai_cin_2();
                    SetConsoleTextAttribute(handle, 7);
                }
            }
            else if (pve_flag = 1)
            {
                if (turn == red)
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出句柄
                    SetConsoleTextAttribute(handle, 4);
                    printf("[info]: 当前为红方");
                    puts("");
                    step = player_cin();
                    SetConsoleTextAttribute(handle, 7);
                }
                else
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出句柄
                    SetConsoleTextAttribute(handle, 1);
                    printf("[info]: 当前为蓝方");
                    puts("");
                    step = ai_cin_1();
                    SetConsoleTextAttribute(handle, 7);
                }
            }
            //进行有效步数的记录
            valid_steps.push_back(step);
            valid_steps.shrink_to_fit();
            //对棋盘进行更新
            board->set_line(turn, step.dir, step.x, step.y);
            //判断长链
            op.dir = step.dir;
            op.x = step.x;
            op.y = step.y;
            draw_line(op);
            //进行换手
            change_round();
            draw_box(board->box);
            clearrectangle(20, 5, 253, 25);
            sprintf_s(tex5, "红方得分 %d : 蓝方得分 %d", red_score, blue_score);
            show_score(20, 5, 20, 10, tex5);
            clearrectangle(517, 52, 700, 72);
            show_who_step(517, 52, 20, 10);
            clearrectangle(260, 5, 600, 25);
            show_where(260, 5, 20, 10);
            //对棋盘得分情况进行更新
            // board->check_box(turn);
            //打印新的棋盘，便于展示
            board->print_color_board();
            //判断当前游戏是否结束
            if (this->is_game_over())
            {
                if (this->get_winner() == this->red)
                {
                    clearrectangle(50, 5, 600, 35);
                    sprintf_s(tex5, "红方获胜！请重新开始游戏!");
                    show_score(50, 5, 30, 20, tex5);
                    printf("[info]: 红方获胜");
                }
                else
                {
                    clearrectangle(50, 5, 600, 35);
                    sprintf_s(tex5, "蓝方获胜！请重新开始游戏!");
                    show_score(50, 5, 30, 20, tex5);
                    printf("[info]: 蓝方获胜");
                }
                //标注位打开，重新开始进入游戏循环
                //同时棋盘复原更新
                this->eve_flag = 0;
                this->pve_flag = 0;
                flag = 0;
                video_flag = 1;
            }
        }
    }

    //撤销操作, 参数为撤销的步数, 自带安全检测
    void undo_step(int undo_step_count)
    {
        if (this->valid_steps.size() < undo_step_count)
        {
            printf("%s***undo failed the len of valid_steps less than %d%s\n", FAIL, undo_step_count, RESET);
            return;
        }
        for (int i = 0; i < undo_step_count; i++)
        {
            //拿出记录
            Step last_step = valid_steps.back();
            ScoreStep last_score = score_steps.back();
            //恢复步数
            board->line[last_step.dir][last_step.x][last_step.y] = 0;
            //恢复回合信息
            turn = last_score.cur_player;
            //返还分数
            if (last_score.cur_player == RED_)
                red_score -= last_score.score;
            else if (last_score.cur_player == BLUE_)
                blue_score -= last_score.score;

            board->refresh_board_undo();
            valid_steps.pop_back();
            score_steps.pop_back();
        }
        printf("%s***undo successfully***%s\n", OKGREEN, RESET);
        MsgShare* q = MsgShare::getQueue();
        q->undo_signal = 1; // 将消息队列中undo信号位改为1

    }

    // 撤回一整个回合, 需要自己体会撤销一步和一个回合的差别
    void undo_last()
    {
        int r_p = board->toggle_round(turn); // 上一步的玩家
        int undo_count = 0;

        if (score_steps.size() == 0)
        {
            printf("%s*** undo failed ***%s\n", FAIL, RESET);
            return;
        }

        int flag = 0;
        for (auto it = score_steps.rbegin(); it != score_steps.rend(); it++)
        {

            if (it->cur_player != r_p && flag == 1)
                break;
            if (it->cur_player != r_p)
                flag = 1;

            if (it->cur_player == r_p && it->score != 0)
                undo_count++;
        }

        if (undo_count == 0)
        {
            undo_step(2);
            return;
        }

        undo_step(undo_count + 2);
    }

    //持久化保存对局信息记录在当前目录的record.json中, 对接比赛要求的棋谱
    void record_info_persistent()
    {
        std::ofstream f;
        f.open("record.json", std::ios::out);



        for (int i = 0; i < valid_steps.size(); i++)
        {
            f << "round: " << i + 1 << "\n";
            if (score_steps[i].cur_player == RED_)
                f << "now turn is: " << "RED_\n";
            else if (score_steps[i].cur_player == BLUE_)
                f << "now turn is: " << "BLUE_\n";
            f << "step is: [" << valid_steps[i].dir << ", " << valid_steps[i].x << " ," << valid_steps[i].y << "]\n";
            f << "score is: " << score_steps[i].score << "\n---\n";

        }

        f.close();
    }

    //持久化保存对局信息记录在当前目录的record.json中, 对接比赛要求的棋谱
    void record_info_persistent(const char* red_name, const char* blue_name)
    {
        time_t now = time(0);
        tm now_time;
        localtime_s(&now_time, &now);
        int y = now_time.tm_year + 1900;
        int m = now_time.tm_mon + 1;
        int d = now_time.tm_mday;
        int min = now_time.tm_min;

        std::string fileName = "record-" + to_string(min);
        fileName.append(".json");

        std::ofstream f;
        f.open(fileName, std::ios::out);

        f << "{ " << "\"R\":" << " \"" << red_name << "\",";
        f << "\"B\":" << " \"" << blue_name << "\",";
        const char* winner = this->red_score > this->blue_score ? "R" : "B";
        f << "\"winner\": " << "\"" << winner << "\",";
        f << "\"RScore\": " << red_score << ",";
        f << "\"BScore\": " << blue_score << ",";
        f << "\"Date\": " << "\"" << y << "-" << m << "-" << d << "\",";
        f << "\"Event\": " << "\"College Student Computer Game Contest\",";
        f << "\"game\": " << "[";

        for (int i = 0; i < valid_steps.size(); i++)
        {

            f << "{\"piece\": ";

            char step_info[7];
            char* mapping_ptr = mapping_to_office(i);
            for (int k = 0; k < 7; k++)
            {
                step_info[k] = *mapping_ptr;
                mapping_ptr++;
            }


            if (i == 59)
            {
                f << "\"";
                for (int j = 0; j < 7; j++)
                    f << step_info[j];
                f << "\"}";
            }
            else
            {
                f << "\"";
                for (int j = 0; j < 7; j++)
                    f << step_info[j];
                f << "\"}, ";
            }

        }

        f << "]" << "}";
        f.close();
        printf("%s\n***json file was saved successfully***\n%s", OKGREEN, RESET);
    }

    //步伐映射, 映射为比赛要求的
    char* mapping_to_office(int i)
    {
        char ans[7];
        if (score_steps.at(i).cur_player == 1)
            ans[0] = 'r';
        else
            ans[0] = 'b';
        ans[1] = '(';


        if (valid_steps[i].dir == 0)
        {
            const char map[6] = "abcde";
            const char map_[11] = "0123456789";
            int x = 6 - valid_steps[i].x;
            char y = map[valid_steps[i].y];
            ans[2] = y;
            ans[3] = map_[x];
            ans[4] = ',';
            ans[5] = 'h';
        }
        else if (valid_steps[i].dir == 1)
        {
            const char map[7] = "abcdef";
            const char map_[11] = "0123456789";
            int y = 5 - valid_steps[i].y;
            char x = map[valid_steps[i].x];
            ans[2] = x;
            ans[3] = map_[y];
            ans[4] = ',';
            ans[5] = 'v';
        }
        ans[6] = ')';
        return ans;
    }

    //图形测试区
    void init_graph()
    {
        Button btns[4];//初始化按钮结构体数组
        initgraph(700, 500, 1);	// 创建绘图窗口，大小为 640x480 像素
        setbkcolor(WHITE);
        cleardevice();
        TCHAR tex1[1000] = _T("人机打人机");
        TCHAR tex2[1000] = _T("PvE 人类先手");
        TCHAR tex3[1000] = _T("PvE 人机先手");
        TCHAR tex4[1000] = _T("撤销");
        setVertexAndShowButton(btns[0], 517, 142, 150, 57, tex1, 15, 0);
        setVertexAndShowButton(btns[1], 517, 242, 150, 57, tex2, 15, 0);
        setVertexAndShowButton(btns[2], 517, 342, 150, 57, tex3, 15, 0);
        setVertexAndShowButton(btns[3], 537, 412, 110, 50, tex4, 15, 0);
        draw();
    }

    //GUI相关
    void draw()
    {
        //设置线条颜色
        setlinecolor(BLACK);
        setlinestyle(PS_SOLID, 2);
        setfillcolor(BLACK);
        for (int i = 0; i <= 5; ++i)
        {

            line(40, 56 + i * 80, 435, 56 + i * 80);
            line(35 + i * 80, 56, 35 + i * 80, 455);
        }
        for (int i = 0; i <= 5; ++i)
        {
            line(40, 65 + i * 80, 435, 65 + i * 80);
            line(45 + i * 80, 56, 45 + i * 80, 455);
        }
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < i + 1; j++)
            {
                solidcircle(39 + i * 80, 60 + j * 80, 10);
                solidcircle(39 + j * 80, 60 + i * 80, 10);

            }

    }

    //GUI相关
    void draw_line(OP op)
    {
        if (op.dir == 0)
        {
            if (op.y < 5)
            {
                //op.x-5
                if (turn == 1)
                {
                    setfillcolor(RGB(255, 99, 71));
                    solidrectangle(50 - 1 + op.y * 80, 55 - 1 + op.x * 80, 110 + op.y * 80, 65 + op.x * 80);
                }
                else {
                    setfillcolor(RGB(65, 105, 225));
                    solidrectangle(50 - 1 + op.y * 80, 55 - 1 + op.x * 80, 110 + op.y * 80, 65 + op.x * 80);
                }
            }
        }
        else if (op.dir == 1)
        {
            if (op.y < 5)
            {


                //op.y-5
                if (turn == 1)
                {
                    setfillcolor(RGB(255, 99, 71));
                    solidrectangle(35 + op.x * 80, 70 + op.y * 80, 45 + op.x * 80, 130 + op.y * 80);
                }
                else
                {
                    setfillcolor(RGB(65, 105, 225));
                    solidrectangle(35 + op.x * 80, 70 + op.y * 80, 45 + op.x * 80, 130 + op.y * 80);
                }
            }
        }
    }

    //GUI相关
    void draw_box(int box[5][5])
    {
        for (int i = 0; i < 5; i++)
            for (int j = 0; j < 5; j++)
            {
                if (box[i][j] == 0)
                {
                    continue;
                }
                if (box[i][j] == 1)
                {
                    setfillcolor(LIGHTRED);
                    solidrectangle(46 + 3 + j * 80, 66 + 3 + i * 80, 46 + 69 - 5 + j * 80, 66 + 69 - 5 + i * 80);
                }
                else
                {
                    setfillcolor(LIGHTBLUE);
                    solidrectangle(46 + 3 + j * 80, 66 + 3 + i * 80, 46 + 69 - 5 + j * 80, 66 + 69 - 5 + i * 80);
                }
            }

    }

    //GUI 显示分数
    void show_score(int x, int y, int len, int wig, char str[1000])
    {
        setbkmode(TRANSPARENT);
        settextstyle(len, wig, _T("宋体"));
        settextcolor(BLACK);
        settextstyle(len, wig, _T("思源字体"));
        outtextxy(x, y, str);
    }

    //GUI实际坐标换算成逻辑坐标
    OP get_clickpoint(int x, int y)
    {
        OP op;
        if (35 <= x && x <= 45)
            op.dir = 1;
        else if (35 + 80 <= x && x <= 45 + 80)
            op.dir = 1;
        else if (35 + 80 + 80 <= x && x <= 45 + 80 + 80)
            op.dir = 1;
        else if (35 + 80 + 80 + 80 <= x && x <= 45 + 80 + 80 + 80)
            op.dir = 1;
        else if (35 + 80 + 80 + 80 + 80 <= x && x <= 45 + 80 + 80 + 80 + 80)
            op.dir = 1;
        else if (35 + 80 + 80 + 80 + 80 + 80 <= x && x <= 45 + 80 + 80 + 80 + 80 + 80)
            op.dir = 1;
        else if (56 <= y && y <= 70)
            op.dir = 0;
        else if (56 + 80 <= y && y <= 70 + 80)
            op.dir = 0;
        else if (56 + 80 + 80 <= y && y <= 70 + 80 + 80)
            op.dir = 0;
        else if (56 + 80 + 80 + 80 <= y && y <= 70 + 80 + 80 + 80)
            op.dir = 0;
        else if (56 + 80 + 80 + 80 + 80 <= y && y <= 70 + 80 + 80 + 80 + 80)
            op.dir = 0;
        else if (56 + 80 + 80 + 80 + 80 + 80 <= y && y <= 70 + 80 + 80 + 80 + 80 + 80)
            op.dir = 0;

        op.x = (x - 35) / 80;
        op.y = (y - 56) / 80;
        if (op.dir == 1)
            return op;
        if (op.dir == 0)
        {
            int temp = op.x;
            op.x = op.y;
            op.y = temp;
            return op;
        }
        else
        {
            return op;
        }
    }
    //GUI 鼠标操作
    void mouseEvent()
    {
        ExMessage msg;
        msg = getmessage(EM_MOUSE);
        switch (msg.message)
        {
        case WM_LBUTTONDOWN:
            //设置标志位，游戏一旦开始不能点击这里
            if (flag == 0)
            {
                if (msg.x >= 517 && msg.x <= 517 + 150 && msg.y >= 142 && msg.y <= 142 + 57)  //EVE
                {
                    printf("[info]: 当前模式为EVE");
                    puts("");
                    eve_flag = 1;
                    turn = 1;
                    break;
                }
                else if (msg.x >= 517 && msg.x <= 517 + 150 && msg.y >= 242 && msg.y <= 242 + 57) //PVE Player_first
                {
                    printf("[info]: 当前游戏模式为PVE");
                    puts("");
                    printf("[info]: 玩家先手设置成功");
                    puts("");
                    pve_flag = 1;
                    turn = 1;
                    break;
                }
                else if (msg.x >= 517 && msg.x <= 517 + 150 && msg.y >= 342 && msg.y <= 342 + 57)//PVE PC_fisst
                {
                    printf("[info]: 当前游戏模式为PVE");
                    puts("");
                    printf("[info]: AI先手设置成功");
                    puts("");
                    pve_flag = 1;
                    turn = 2;
                    break;
                }
                //else if(msg.x >= 517 && msg.x <= 517 + 150 && msg.y >= 342 && msg.y <= 342 + 57)//PVE PC_fisst
                // 悔棋的位置后面处理
            }
            if (msg.x >= 537 && msg.x <= 537 + 110 && msg.y >= 412 && msg.y <= 412 + 50) //roll_back
            {
                if (pve_flag == 1 && turn == 1)
                {
                    //undo_step(2);
                    undo_last();
                    board->print_color_board();
                    clearrectangle(30, 52, 444, 480);
                    draw();
                    draw_line_new();

                    draw_box(board->box);
                }
            }
            op = get_clickpoint(msg.x, msg.y);
            break;
        }
    }
    void draw_line_new()
    {

        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 5; j++)
            {
                if (board->line[0][i][j] == 1)
                {
                    setfillcolor(RGB(255, 99, 71));
                    solidrectangle(50 - 1 + j * 80, 55 - 1 + i * 80, 110 + j * 80, 65 + i * 80);
                }
                else if (board->line[0][i][j] == 2)
                {
                    setfillcolor(RGB(65, 105, 225));
                    solidrectangle(50 - 1 + j * 80, 55 - 1 + i * 80, 110 + j * 80, 65 + i * 80);
                }
            }
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 5; j++)
            {
                if (board->line[1][i][j] == 1)
                {
                    setfillcolor(RGB(255, 99, 71));
                    solidrectangle(35 + i * 80, 70 + j * 80, 45 + i * 80, 130 + j * 80);
                }
                else if (board->line[1][i][j] == 2)
                {
                    setfillcolor(RGB(65, 105, 225));
                    solidrectangle(35 + i * 80, 70 + j * 80, 45 + i * 80, 130 + j * 80);
                }
            }

    }


    //设置游戏的solver, 一个游戏有两个solver, 在人机模式下会启用solver_1
    void set_solver(TemplateSolver* solver_1, TemplateSolver* solver_2)
    {
        if (solver_1 == nullptr || solver_2 == nullptr)
            return;

        this->solver_1 = solver_1;
        this->solver_2 = solver_2;
    }

    //指定设置游戏的solver, 一个游戏有两个solver, 在人机模式下会启用solver_1
    void set_solver(TemplateSolver* solver, int which)
    {
        if (solver == nullptr)
            return;

        if (which == 1)
        {
            solver_1 = solver;
        }

        if (which == 2)
        {
            solver_2 = solver;
        }
    }

    // 设置solver_1 换了个方式而已
    void set_pve_solver(TemplateSolver* solver)
    {
        set_solver(solver, 1);
    }

    // 设置游戏起始回合默认红方先手
    GameManager* set_turn(int turn)
    {
        this->turn = turn;
    }


    // 返回最后谁赢了, 0代表平局, 1代表s1赢, 2代表s2赢
    int task_loop(TemplateSolver* s1, TemplateSolver* s2, int times, int cfg)
    {
        set_solver(s1, s2);

        int s1_win_times = 0;
        int s2_win_times = 0;

        int turn_flag = 1;

        // 任务loop
        for (int i = 0; i < times; i++)
        {
            while (true)
            {
                Step step;
                if (turn == red)
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
                    SetConsoleTextAttribute(handle, 4);
                    printf("[info]: 当前回合为红方");
                    puts("");
                    step = ai_cin_1();
                    SetConsoleTextAttribute(handle, 7);
                }
                else
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
                    SetConsoleTextAttribute(handle, 1);
                    printf("[info]: 当前回合为蓝方");
                    puts("");
                    step = ai_cin_2();
                    SetConsoleTextAttribute(handle, 7);
                }
                //进行有效步数的记录
                valid_steps.push_back(step);
                valid_steps.shrink_to_fit();
                //对棋盘进行更新
                board->set_line(turn, step.dir, step.x, step.y);

                change_round();
                board->print_color_board();
                if (this->is_game_over())
                {
                    if (this->get_winner() == this->red)
                    {
                        printf("[info]: 红方获胜");
                        s1_win_times++;
                    }
                    else
                    {
                        printf("[info]: 蓝方获胜");
                        s2_win_times++;
                    }
                    restart(turn_flag == 1 ? 2 : 1);
                    break;
                }
            }
        }

        if (s1_win_times == s2_win_times)
            return 0;

        return s1_win_times > s2_win_times ? 1 : 2;
    }


    //原本为了统计solver胜率而写的, 但现在看来用处不大, 并且问题很多, 但当时费了很长时间(写了多线程版本)
    void task_loop(TemplateSolver* s1, TemplateSolver* s2, int times = 1)
    {
        set_solver(s1, s2);

        int s1_win_times = 0;
        int s2_win_times = 0;

        int turn_flag = 1;


        // 任务loop
        for (int i = 0; i < times; i++)
        {
            while (true)
            {
                Step step;
                if (turn == red)
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
                    SetConsoleTextAttribute(handle, 4);
                    printf("[info]: 当前回合为红方");
                    puts("");
                    step = ai_cin_1();
                    SetConsoleTextAttribute(handle, 7);
                }
                else
                {
                    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
                    SetConsoleTextAttribute(handle, 1);
                    printf("[info]: 当前回合为蓝方");
                    puts("");
                    step = ai_cin_2();
                    SetConsoleTextAttribute(handle, 7);
                }
                //进行有效步数的记录
                valid_steps.push_back(step);
                valid_steps.shrink_to_fit();
                //对棋盘进行更新
                board->set_line(turn, step.dir, step.x, step.y);

                change_round();
                board->print_color_board();
                if (this->is_game_over())
                {
                    if (this->get_winner() == this->red)
                    {
                        printf("[info]: 红方获胜");
                        s1_win_times++;
                    }
                    else
                    {
                        printf("[info]: 蓝方获胜");
                        s2_win_times++;
                    }
                    restart(turn_flag == 1 ? 2 : 1);
                    break;
                }
            }
        }

        std::mutex mtx; // 加锁
        mtx.lock();

        //写文件, 记录信息
        time_t now = time(0);
        tm now_time;
        localtime_s(&now_time, &now);
        int y = now_time.tm_year + 1900;
        int m = now_time.tm_mon + 1;
        int d = now_time.tm_mday;
        int h = now_time.tm_hour;
        int min = now_time.tm_min;
        int s = now_time.tm_sec;
        ofstream f;

        std::string time_str = std::to_string(y) + "-" + std::to_string(m) + "-" + std::to_string(d) + "-" + std::to_string(h) + "-" + std::to_string(min) + "-" + std::to_string(s);

        std::string path = "./data/" + time_str + ".txt";
        f.open(path, std::ios::out);

        double rate_1 = static_cast<double>(s1_win_times) / times;
        double rate_2 = static_cast<double>(s2_win_times) / times;

        f << time_str << "\n";
        f << "times: " << times << "\n";
        f << "solver_1 win times: " << s1_win_times << "\n";
        f << "solver_1 win rate: " << rate_1 << "\n";
        f << "solver_2 win times: " << s2_win_times << "\n";
        f << "solver_2 win rate: " << rate_2 << "\n";

        if (s1_win_times >= s2_win_times)
            f << "task_winner: solver1\n";
        else
            f << "task_winner: solver2\n";

        f << "solver1 config:\n";
        s1->record_solver_info(f);
        f << "solver2 config:\n";
        s2->record_solver_info(f);

        f.close();

        printf("%s\n***task info file was saved successfully***\n%s", OKGREEN, RESET);
        printf("%spath: %s%s", OKGREEN, path.c_str(), RESET);

        mtx.unlock(); // 解锁
    }


    //对接通信平台, 调用的是solver_1生成步伐
    void platform_loop()
    {

        Step step = {-1 , -1, -1};//临时步结构
        char message[256];//通信消息缓冲
        int computerSide;//己方执棋颜色
        int start = 0;//对局开始标记
        int our_side = 0;
        int they_side = 0;

        while (true) // 程序主循环
        {
            fflush(stdout);


            //获取平台消息
            std::cin >> message;

            //分析命令
            if (strcmp(message, "move") == 0)
            {
                int num; // 行棋数量

                std::cin >> num;
                std::cin >> message;
                fflush(stdin);

                for (int i = 0; i < num; ++i)
                {
                    step.dir = message[i * 3] - 'A';
                    step.x = message[i * 3 + 1] - 'A';
                    step.y = message[i * 3 + 2] - 'A';

                    //处理对手行棋
                    board->set_line(turn, step.dir, step.x, step.y);
                    int score = board->check_box(turn);
                    if (score == 0)
                        turn = board->toggle_round(turn);
                }

                //我们着棋
                vector<Step> moves;
                while (true)
                {
                    step = solver_1->solve(get_frame());
                    board->set_line(turn, step.dir, step.x, step.y);
                    moves.push_back(step);
                    int score = board->check_box(turn);
                    if (score == 0 || board->get_available_steps().size() == 0)
                        break;
                }
                    
                turn = board->toggle_round(turn); // 换手


                char next[256];
                for (int i = 0; i < moves.size(); i++)
                {
                    next[i * 3] = 'A' + moves[i].dir;
                    next[i * 3 + 1] = 'A' + moves[i].x;
                    next[i * 3 + 2] = 'A' + moves[i].y;
                }
                next[moves.size() * 3] = '\0';
                printf("move %d %s\n", moves.size(), next);


            }
            else if (strcmp(message, "name?") == 0)//询问引擎名
            {
                fflush(stdin);

                //输出引擎名
                printf("name beta flying\n");
            }
            else if (strcmp(message, "quit") == 0)//退出引擎
            {
                fflush(stdin);

                //...
                printf("Quit!\n");
                break;
            }
            else if (strcmp(message, "end") == 0)//对局结束
            {
                fflush(stdin);
                restart(1);
                start = 0;
            }
            else if (strcmp(message, "new") == 0)//建立新棋局
            {
                std::cin >> message;
                fflush(stdin);
                restart(1);
                if (strcmp(message, "black") == 0)//执黑
                {
                    turn = RED_;
                    our_side = RED_;
                    they_side = BLUE_;
                }
                else//执白
                {
                    turn = RED_;
                    our_side = BLUE_;
                    they_side = RED_;
                }
                //初始化棋局
                start = 1;

                if (our_side == RED_)
                {
                    int num = 1;
                    //生成着法，并保存在线的数量在num中，保存线的坐标列表在message中
                    step = solver_1->solve(get_frame());
                    board->set_line(turn, step.dir, step.x, step.y);
                    board->check_box(turn);
                    turn = board->toggle_round(turn);
                    //处理己方行棋
                    char next[4] = {'0', '0', '0', '\0'};
                    next[0] = step.dir + 'A';
                    next[1] = step.x + 'A';
                    next[2] = step.y + 'A';

                    //输出着法
                    printf("move %d %s\n", num, next);
                }
            }
        }
    }

    //得到task_loop的包装线程对象, 线程对象应该从不同的GameManager实例中获取
    std::thread get_thread_task_loop(TemplateSolver* s1, TemplateSolver* s2, int times = 1)
    {
        return std::thread(static_cast<void(GameManager::*)(TemplateSolver * s1, TemplateSolver * s2, int times)>(&GameManager::task_loop), this, s1, s2, times);
    }

    // 统计一堆solver中的胜率最高的那个, 但现在已经没怎么使用了
    void select_best_solver(vector<TemplateSolver*>* solvers, int times = 11)
    {
        //锁
        std::mutex mtx;


        //打乱solvers容器
        std::random_device seed;
        auto rng = std::default_random_engine{ seed() };
        std::shuffle(solvers->begin(), solvers->end(), rng);

        time_t now = time(0);
        tm now_time;
        localtime_s(&now_time, &now);
        int y = now_time.tm_year + 1900;
        int m = now_time.tm_mon + 1;
        int d = now_time.tm_mday;
        int h = now_time.tm_hour;
        int min = now_time.tm_min;
        int s = now_time.tm_sec;

        mtx.lock();
        ofstream f;
        std::string time_str = std::to_string(y) + "-" + std::to_string(m) + "-" + std::to_string(d) + "-" + std::to_string(h) + "-" + std::to_string(min) + "-" + std::to_string(s);
        std::string path = "./data/" + time_str + "select" + ".txt";
        f.open(path, std::ios::out);
        mtx.unlock();

        //结果数组
        int* result = new int[solvers->size()]();

        //记录每个的对局情况
        for (int i = 0; i < solvers->size(); i++)
            for (int j = 0; j < solvers->size(); j++)
            {
                //不和自己打
                if (i == j)
                    continue;

                int winner = task_loop(solvers->at(i), solvers->at(j), times, cfg::NOT_RECORD);

                mtx.lock();
                f << "solver[" << i << "] " << "vs " << "solver[" << j << "]: \n";
                if (winner == 0)
                    f << "result: " << "***draw***\n";
                else if (winner == 1)
                {
                    f << "result: " << "solver<" << i << "> win\n";
                    result[i] += 1;
                }
                else if (winner == 2)
                {
                    f << "result: " << "solver<" << j << "> win\n";
                    result[j] += 1;
                }
                mtx.unlock();
            }

        //结束 记录每个solver的配置
        int max_score = result[0];
        int index = 0;
        for (int i = 0; i < solvers->size(); i++)
        {
            mtx.lock();
            f << "solver[" << i << "] config:\n";
            solvers->at(i)->record_solver_info(f);
            f << "---\n";
            mtx.unlock();

            if (result[i] > max_score)
            {
                index = i;
                max_score = result[i];
            }

        }

        mtx.lock();
        f << "#best solver is : " << "solver[" << index << "], score : " << max_score << "\n";
        mtx.unlock();
        delete[] result;
        printf("%stask exit 0%s", OKGREEN, RESET);
    }

    //包装线程对象
    thread get_select_task(vector<TemplateSolver*>* solvers, int times = 11)
    {
        return thread(static_cast<void(GameManager::*)(vector<TemplateSolver*>*solvers, int times)>(&GameManager::select_best_solver), this, solvers, times);
    }

    // GUI相关
    void show_who_step(int x, int y, int len, int wig)
    {
        setbkmode(TRANSPARENT);
        settextstyle(len, wig, _T("思源字体"));
        if (turn == 1)
        {
            char str[] = "当前玩家为：红方";
            outtextxy(x, y, str);
        }
        if (turn == 2)
        {
            char str[] = "当前玩家为：蓝方";
            outtextxy(x, y, str);
        }

    }

    // GUI相关
    void show_where(int x, int y, int len, int wig)
    {
        setbkmode(TRANSPARENT);
        settextstyle(len, wig, _T("思源字体"));
        char str[100];

        if (!valid_steps.empty())
        {
            auto it_ = valid_steps.back();

            sprintf_s(str, "当前步伐 (%d,%d,%d)", it_.dir, it_.x, it_.y);
            outtextxy(x, y, str);
        }
    }
};
